home *** CD-ROM | disk | FTP | other *** search
- (*----------------------------------------------------------------------*)
- (* PIBASYNC.PAS --- Asynchronous I/O for Turbo Pascal *)
- (*----------------------------------------------------------------------*)
- (* *)
- (* Author: Philip R. Burns *)
- (* *)
- (* Version: 1.0 (January, 1985) *)
- (* 2.0 (June, 1985) *)
- (* 2.1 (July, 1985) *)
- (* *)
- (* Systems: For MS-DOS on IBM PCs and close compatibles only. *)
- (* Note: I have checked these on Zenith 151s under *)
- (* MSDOS 2.1 and IBM PCs under PCDOS 2.0. *)
- (* *)
- (* History: Some of these routines are based upon ones written by: *)
- (* *)
- (* Alan Bishop *)
- (* C. J. Dunford *)
- (* Michael Quinlan *)
- (* *)
- (* I have cleaned up these other authors' code, fixed some *)
- (* bugs, and added many new features. *)
- (* *)
- (* Suggestions for improvements or corrections are welcome. *)
- (* Please leave messages on Gene Plantz's BBS (312) 882 4145 *)
- (* or Ron Fox's BBS (312) 940 6496. *)
- (* *)
- (* If you use this code in your own programs, please be nice *)
- (* and give all of us credit. *)
- (* *)
- (*----------------------------------------------------------------------*)
- (* *)
- (* Routines: *)
- (* *)
- (* Async_Init --- Performs initialization. *)
- (* Async_Clear_Errors --- Clear pending serial port errors *)
- (* Async_Reset_Port --- Resets UART parameters for port *)
- (* Async_Open --- Sets up COM port *)
- (* Async_Close --- Closes down COM port *)
- (* Async_Carrier_Detect --- Checks for modem carrier detect *)
- (* Async_Carrier_Drop --- Checks for modem carrier drop *)
- (* Async_Buffer_Check --- Checks if character in COM buffer *)
- (* Async_Buffer_Full --- Checks if async buffer nearly full *)
- (* Async_Term_Ready --- Toggles terminal ready status *)
- (* Async_Receive --- Reads character from COM buffer *)
- (* Async_Receive_With_Timeout *)
- (* --- Receives char. with timeout check *)
- (* Async_Ring_Detect --- If ringing detected *)
- (* Async_Send --- Transmits char over COM port *)
- (* Async_Send_String --- Sends string over COM port *)
- (* Async_Send_String_With_Delays *)
- (* --- Sends string with timed delays *)
- (* Async_Send_Break --- Sends break (attention) signal *)
- (* Async_Percentage_Used --- Returns percentage com buffer used *)
- (* Async_Purge_Buffer --- Purges receive buffer *)
- (* *)
- (*----------------------------------------------------------------------*)
- (* *)
- (*----------------------------------------------------------------------*)
-
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* COMMUNICATIONS HARDWARE ADDRESSES *)
- (* *)
- (* These are specific to IBM PCs and close compatibles. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- CONST
-
-
- UART_THR = $00; (* offset from base of UART Registers for IBM PC *)
- UART_RBR = $00;
- UART_IER = $01;
- UART_IIR = $02;
- UART_LCR = $03;
- UART_MCR = $04;
- UART_LSR = $05;
- UART_MSR = $06;
-
- I8088_IMR = $21; (* port address of the Interrupt Mask Register *)
-
- COM1_Base = $03F8; (* port addresses for the UART *)
- COM2_Base = $02F8;
-
- COM1_Irq = 4; (* Interrupt line for the UART *)
- COM2_Irq = 3;
-
- CONST
-
- Async_DSeg_Save : INTEGER = 0; (* Save DS reg in Code Segment for *)
- (* interrupt routine *)
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* COMMUNICATIONS BUFFER VARIABLES *)
- (* *)
- (* The Communications Buffer is implemented as a circular (ring) *)
- (* buffer, or double-ended queue. The asynchronous I/O routines *)
- (* enter characters in the buffer as they are received. Higher- *)
- (* level routines may extract characters from the buffer. *)
- (* *)
- (* Note that this buffer is used for input only; output is done *)
- (* on a character-by-character basis. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- CONST
-
- Async_Buffer_Max = 4095; (* Size of Communications Buffer *)
- TimeOut = 256; (* TimeOut value *)
-
- VAR
- (* Communications Buffer Itself *)
-
- Async_Buffer : ARRAY[0..Async_Buffer_Max] OF CHAR;
-
- Async_Open_Flag : BOOLEAN; (* true if Open but no Close *)
- Async_Port : INTEGER; (* current Open port number (1 or 2) *)
- Async_Base : INTEGER; (* base for current open port *)
- Async_Irq : INTEGER; (* irq for current open port *)
-
- Async_Buffer_Overflow : BOOLEAN; (* True if buffer overflow has happened *)
- Async_Buffer_Used : INTEGER;
- Async_MaxBufferUsed : INTEGER;
-
- (* Async_Buffer empty if Head = Tail *)
- Async_Buffer_Head : INTEGER; (* Loc in Async_Buffer to put next char *)
- Async_Buffer_Tail : INTEGER; (* Loc in Async_Buffer to get next char *)
- Async_Buffer_NewTail : INTEGER;
-
- Async_XOFF_Sent : BOOLEAN (* If XOFF sent *);
- Async_Baud_Rate : INTEGER (* Current baud rate *);
-
- CONST
- Async_XON : CHAR = ^Q (* XON character *);
- Async_XOFF : CHAR = ^S (* XOFF character *);
-
- (*----------------------------------------------------------------------*)
- (* BIOS_RS232_Init --- Initialize UART *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE BIOS_RS232_Init( ComPort, ComParm : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: BIOS_RS232_Init *)
- (* *)
- (* Purpose: Issues interrupt $14 to initialize the UART *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* BIOS_RS232_Init( ComPort, ComParm : INTEGER ); *)
- (* *)
- (* ComPort --- Communications Port Number (1 or 2) *)
- (* ComParm --- Communications Parameter Word *)
- (* *)
- (* Calls: INTR (to perform BIOS interrupt $14) *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Regs: RegPack;
-
- BEGIN (* BIOS_RS232_Init *)
-
- WITH Regs DO
- BEGIN
- Ax := ComParm AND $00FF; (* AH=0; AL=ComParm *)
- Dx := ComPort; (* Port number to use *)
- INTR($14, Regs);
- END;
-
- END (* BIOS_RS232_Init *);
-
-
- (*----------------------------------------------------------------------*)
- (* DOS_Set_Intrpt --- Call DOS to set interrupt vector *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE DOS_Set_Intrpt( v, s, o : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: DOS_Set_Intrpt *)
- (* *)
- (* Purpose: Calls DOS to set interrupt vector *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* DOS_Set_Intrpt( v, s, o : INTEGER ); *)
- (* *)
- (* v --- interrupt vector number to set *)
- (* s --- segment address of interrupt routine *)
- (* o --- offset address of interrupt routine *)
- (* *)
- (* Calls: MSDOS (to set interrupt) *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Regs : Regpack;
-
- BEGIN (* DOS_Set_Intrpt *)
-
- WITH Regs DO
- BEGIN
- Ax := $2500 + ( v AND $00FF );
- Ds := s;
- Dx := o;
- MsDos( Regs );
- END;
-
- END (* DOS_Set_Intrpt *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Isr --- Interrupt Service Routine *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Isr;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Isr *)
- (* *)
- (* Purpose: Invoked when UART has received character from *)
- (* communications line (asynchronous) *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Isr; *)
- (* *)
- (* --- Called asyncronously only!!!!!! *)
- (* *)
- (* Remarks: *)
- (* *)
- (* This is Michael Quinlan's version of the interrupt handler. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Isr *)
-
- (* NOTE: on entry, Turbo Pascal has already PUSHed BP and SP *)
-
- INLINE(
- (* save all registers used *)
-
- $50/$53/$51/$52/$56/$57/$1E/$06/$FB/
-
- (* set up the DS register to point to Turbo Pascal's data segment *)
- $2E/$FF/$36/Async_Dseg_Save/ (* PUSH CS:Async_Dseg_Save *)
- $1F/ (* POP DS *)
- (* get the incoming character *)
- (* Async_Buffer[Async_Buffer_Head] := Chr(Port[UART_RBR + Async_Base]); *)
- $8B/$16/Async_Base/ (* MOV DX,Async_Base *)
- $EC/ (* IN AL,DX *)
- $8B/$1E/Async_Buffer_Head/ (* MOV BX,Async_Buffer_Head *)
- $88/$87/Async_Buffer/ (* MOV Async_Buffer[BX],AL *)
- (* Async_Buffer_NewHead := Async_Buffer_Head + 1; *)
- $43/ (* INC BX *)
- (* if Async_Buffer_NewHead > Async_Buffer_Max then
- Async_Buffer_NewHead := 0; *)
- $81/$FB/Async_Buffer_Max/ (* CMP BX,Async_Buffer_Max *)
- $7E/$02/ (* JLE L001 *)
- $33/$DB/ (* XOR BX,BX *)
- (* if Async_Buffer_NewHead = Async_Buffer_Tail then
- Async_Buffer_Overflow := TRUE
- else *)
- (*L001:*)
- $3B/$1E/Async_Buffer_Tail/ (* CMP BX,Async_Buffer_Tail *)
- $75/$08/ (* JNE L002 *)
- $C6/$06/Async_Buffer_Overflow/$01/ (* MOV Async_Buffer_Overflow,1 *)
- $90/ (* NOP generated by assembler for some reason *)
- $EB/$16/ (* JMP SHORT L003 *)
- (* begin
- Async_Buffer_Head := Async_Buffer_NewHead;
- Async_Buffer_Used := Async_Buffer_Used + 1;
- if Async_Buffer_Used > Async_MaxBufferUsed then
- Async_MaxBufferUsed := Async_Buffer_Used
- end; *)
- (*L002:*)
- $89/$1E/Async_Buffer_Head/ (* MOV Async_Buffer_Head,BX *)
- $FF/$06/Async_Buffer_Used/ (* INC Async_Buffer_Used *)
- $8B/$1E/Async_Buffer_Used/ (* MOV BX,Async_Buffer_Used *)
- $3B/$1E/Async_MaxBufferUsed/ (* CMP BX,Async_MaxBufferUsed *)
- $7E/$04/ (* JLE L003 *)
- $89/$1E/Async_MaxBufferUsed/ (* MOV Async_MaxBufferUsed,BX *)
- (*L003:*)
- (* disable interrupts *)
- $FA/ (* CLI *)
- (* Port[$20] := $20; *) (* use non-specific EOI *)
- $B0/$20/ (* MOV AL,20h *)
- $E6/$20/ (* OUT 20h,AL *)
- (* restore the registers then use IRET to return *)
- (* the last two POPs are required because Turbo Pascal PUSHes these regs
- before we get control. The manual doesn't say so, but that is what
- really happens *)
- $07/$1F/$5F/$5E/$5A/$59/$5B/$58/$8B/$E5/$5D/
- $CF) (* IRET *)
-
- END (* Async_Isr *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Init --- Initialize Asynchronous VARiables *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Init;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Init *)
- (* *)
- (* Purpose: Initializes variables *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Init; *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Init *)
-
- Async_DSeg_Save := DSeg;
- Async_Open_Flag := FALSE;
- Async_Buffer_Overflow := FALSE;
- Async_Buffer_Used := 0;
- Async_MaxBufferUsed := 0;
- Async_XOFF_Sent := FALSE;
- Async_Buffer_Head := 0;
- Async_Buffer_Tail := 0;
-
- END (* Async_Init *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Close --- Close down communications interrupts *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Close;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Close *)
- (* *)
- (* Purpose: Resets interrupt system when UART interrupts *)
- (* are no longer needed. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Close; *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- i : INTEGER;
- m : INTEGER;
-
- BEGIN (* Async_Close *)
-
- IF Async_Open_Flag THEN
- BEGIN
-
- (* disable the IRQ on the 8259 *)
-
- INLINE($FA); (* disable interrupts *)
-
- i := Port[I8088_IMR]; (* get the interrupt mask register *)
- m := 1 SHL Async_Irq; (* set mask to turn off interrupt *)
- Port[I8088_IMR] := i OR m;
-
- (* disable the 8250 data ready interrupt *)
-
- Port[UART_IER + Async_Base] := 0;
-
- (* disable OUT2 on the 8250 *)
-
- Port[UART_MCR + Async_Base] := 0;
-
- INLINE($FB); (* enable interrupts *)
-
- (* re-initialize our data areas so we know *)
- (* the port is closed *)
-
- Async_Open_Flag := FALSE;
- Async_XOFF_Sent := FALSE;
-
- END;
-
- END (* Async_Close *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Clear_Errors --- Reset pending errors in async port *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Clear_Errors;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Clear_Errors *)
- (* *)
- (* Purpose: Resets pending errors in async port *)
- (* *)
- (* Calling sequence: *)
- (* *)
- (* Async_Clear_Errors; *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- I: INTEGER;
- M: INTEGER;
-
- BEGIN (* Async_Clear_Errors *)
-
- (* Read the RBR and reset any pending error conditions. *)
- (* First turn off the Divisor Access Latch Bit to allow *)
- (* access to RBR, etc. *)
-
- INLINE($FA); (* disable interrupts *)
-
- Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] AND $7F;
-
- (* Read the Line Status Register to reset any errors *)
- (* it indicates *)
-
- I := Port[UART_LSR + Async_Base];
-
- (* Read the Receiver Buffer Register in case it *)
- (* contains a character *)
-
- I := Port[UART_RBR + Async_Base];
-
- (* enable the irq on the 8259 controller *)
-
- I := Port[I8088_IMR]; (* get the interrupt mask register *)
- M := (1 SHL Async_Irq) XOR $00FF;
-
- Port[I8088_IMR] := I AND M;
-
- (* enable the data ready interrupt on the 8250 *)
-
- Port[UART_IER + Async_Base] := $01;
-
- (* enable OUT2 on 8250 *)
-
- I := Port[UART_MCR + Async_Base];
- Port[UART_MCR + Async_Base] := I OR $08;
-
- INLINE($FB); (* enable interrupts *)
-
- END (* Async_Clear_Errors *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Reset_Port --- Set/reset communications port parameters *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Reset_Port( ComPort : INTEGER;
- BaudRate : INTEGER;
- Parity : CHAR;
- WordSize : INTEGER;
- StopBits : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Reset_Port *)
- (* *)
- (* Purpose: Resets communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Reset_Port( ComPort : INTEGER; *)
- (* BaudRate : INTEGER; *)
- (* Parity : CHAR; *)
- (* WordSize : INTEGER; *)
- (* StopBits : INTEGER); *)
- (* *)
- (* ComPort --- which port (1 or 2) *)
- (* BaudRate --- Baud rate (110 to 9600) *)
- (* Parity --- "E" for even, "O" for odd, "N" for none *)
- (* WordSize --- Bits per character (5 through 8) *)
- (* StopBits --- How many stop bits (1 or 2) *)
- (* *)
- (* Calls: *)
- (* *)
- (* Async_Clear_Errors --- Clear async line errors *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- CONST (* Baud Rate Constants *)
-
- Async_Num_Bauds = 8;
-
- Async_Baud_Table : ARRAY [1..Async_Num_Bauds] OF RECORD
- Baud, Bits : INTEGER;
- END
-
- = ( ( Baud: 110; Bits: $00 ),
- ( Baud: 150; Bits: $20 ),
- ( Baud: 300; Bits: $40 ),
- ( Baud: 600; Bits: $60 ),
- ( Baud: 1200; Bits: $80 ),
- ( Baud: 2400; Bits: $A0 ),
- ( Baud: 4800; Bits: $C0 ),
- ( Baud: 9600; Bits: $E0 ) );
-
- VAR
- I : INTEGER;
- M : INTEGER;
- ComParm : INTEGER;
-
- BEGIN (* Async_Reset_Port *)
-
- (*---------------------------------------------------*)
- (* Build the ComParm for RS232_Init *)
- (* See Technical Reference Manual for description *)
- (*---------------------------------------------------*)
-
- (* Set up the bits for the baud rate *)
-
- IF BaudRate > 9600 THEN
- BaudRate := 9600
- ELSE IF BaudRate <= 0 THEN
- BaudRate := 300;
- (* Remember baud rate for purges *)
- Async_Baud_Rate := BaudRate;
-
- I := 0;
-
- REPEAT
- I := I + 1
- UNTIL ( ( I >= Async_Num_Bauds ) OR
- ( BaudRate = Async_Baud_Table[I].Baud ) );
-
- ComParm := Async_Baud_Table[I].Bits;
-
- (* Choose Parity *)
-
- IF Parity In ['E', 'e'] THEN
- ComParm := ComParm or $0018
- ELSE IF Parity In ['O', 'o'] THEN
- ComParm := ComParm or $0008;
-
- (* Choose number of data bits *)
-
- WordSize := WordSize - 5;
-
- IF ( WordSize < 0 ) OR ( WordSize > 3 ) THEN
- WordSize := 3;
-
- ComParm := ComParm OR WordSize;
-
- (* Choose stop bits *)
-
- IF StopBits = 2 THEN
- ComParm := ComParm OR $0004; (* default is 1 stop bit *)
-
- (* use the BIOS COM port initialization routine *)
-
- BIOS_RS232_Init( ComPort - 1 , ComParm );
-
- (* Clear any pending errors on async line *)
-
- Async_Clear_Errors;
-
- END (* Async_Reset_Port *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Open --- Open communications port *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Open( ComPort : INTEGER;
- BaudRate : INTEGER;
- Parity : CHAR;
- WordSize : INTEGER;
- StopBits : INTEGER ) : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Open *)
- (* *)
- (* Purpose: Opens communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Open( ComPort : INTEGER; *)
- (* BaudRate : INTEGER; *)
- (* Parity : CHAR; *)
- (* WordSize : INTEGER; *)
- (* StopBits : INTEGER) : BOOLEAN; *)
- (* *)
- (* ComPort --- which port (1 or 2) *)
- (* BaudRate --- Baud rate (110 to 9600) *)
- (* Parity --- "E" for even, "O" for odd, "N" for none *)
- (* WordSize --- Bits per character (5 through 8) *)
- (* StopBits --- How many stop bits (1 or 2) *)
- (* *)
- (* Flag returned TRUE if port initialized successfully; *)
- (* Flag returned FALSE if any errors. *)
- (* *)
- (* Calls: *)
- (* *)
- (* Async_Reset_Port --- initialize RS232 port *)
- (* DOS_Set_Intrpt --- set address of RS232 interrupt routine *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Open *)
- (* IF port open, close it down first. *)
-
- IF Async_Open_Flag THEN Async_Close;
-
- (* Choose communications port *)
- IF ComPort = 2 THEN
- BEGIN
- Async_Port := 2;
- Async_Base := COM2_Base;
- Async_Irq := COM2_Irq;
- END
- ELSE
- BEGIN
- Async_Port := 1; (* default to COM1 *)
- Async_Base := COM1_Base;
- Async_Irq := COM1_Irq;
- END;
-
- IF (Port[UART_IIR + Async_Base] and $00F8) <> 0 THEN
- Async_Open := FALSE (* Serial port not installed *)
- ELSE
- BEGIN (* Open the port *)
- (* Set up UART *)
-
- Async_Reset_Port( ComPort, BaudRate, Parity, WordSize, StopBits );
-
- (* Set interrupt routine address *)
-
- DOS_Set_Intrpt( Async_Irq + 8 , CSeg , Ofs( Async_Isr ) );
-
- (* Clear any pending errors *)
- Async_Clear_Errors;
-
- Async_Open := TRUE;
- Async_Open_Flag := TRUE;
-
- END;
-
- END (* Async_Open *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Carrier_Detect --- Check for modem carrier detect *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Carrier_Detect : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Carrier_Detect *)
- (* *)
- (* Purpose: Looks for modem carrier detect *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Carrier_Detect : BOOLEAN; *)
- (* *)
- (* Flag is set TRUE if carrier detected, else FALSE. *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Carrier_Detect *)
-
- Async_Carrier_Detect := ODD( Port[ UART_MSR + Async_Base ] SHR 7 );
-
- END (* Async_Carrier_Detect *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Carrier_Drop --- Check for modem carrier drop/timeout *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Carrier_Drop : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Carrier_Drop *)
- (* *)
- (* Purpose: Looks for modem carrier drop/timeout *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Carrier_Drop : BOOLEAN; *)
- (* *)
- (* Flag is set TRUE if carrier dropped, else FALSE. *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Carrier_Drop *)
-
- Async_Carrier_Drop := NOT ODD( Port[ UART_MSR + Async_Base ] SHR 7 );
-
- END (* Async_Carrier_Drop *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Term_Ready --- Set terminal ready status *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Term_Ready( Ready_Status : BOOLEAN );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Term_Ready *)
- (* *)
- (* Purpose: Sets terminal ready status *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Term_Ready( Ready_Status : BOOLEAN ); *)
- (* *)
- (* Ready_Status --- Set TRUE to set terminal ready on, *)
- (* Set FALSE to set terminal ready off. *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Mcr_Value: BYTE;
-
- BEGIN (* Async_Term_Ready *)
-
- Mcr_Value := Port[ UART_MCR + Async_Base ];
-
- IF ODD( Mcr_Value ) THEN Mcr_Value := Mcr_Value - 1;
-
- IF Ready_Status THEN Mcr_Value := Mcr_Value + 1;
-
- Port[ UART_MCR + Async_Base ] := Mcr_Value;
-
- END (* Async_Term_Ready *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Buffer_Check --- Check if character in buffer *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Buffer_Check : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Buffer_Check *)
- (* *)
- (* Purpose: Check if character in buffer *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Buffer_Check : BOOLEAN; *)
- (* *)
- (* Flag returned TRUE if character received in buffer, *)
- (* Flag returned FALSE if no character received. *)
- (* *)
- (* Calls: None *)
- (* *)
- (* Remarks: *)
- (* *)
- (* This routine only checks if a character has been received *)
- (* and thus can be read; it does NOT return the character. *)
- (* Use Async_Receive to read the character. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Buffer_Check *)
-
- Async_Buffer_Check := ( Async_Buffer_Head <> Async_Buffer_Tail );
-
- END (* Async_Buffer_Check *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Receive --- Return character from buffer *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Receive( VAR C : Char ) : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Receive *)
- (* *)
- (* Purpose: Retrieve character (if any) from buffer *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Receive( VAR C: Char ) : BOOLEAN; *)
- (* *)
- (* C --- character (if any) retrieved from buffer; *)
- (* set to CHR(0) if no character available. *)
- (* *)
- (* Flag returned TRUE if character retrieved from buffer, *)
- (* Flag returned FALSE if no character retrieved. *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Receive *)
-
- IF Async_Buffer_Head = Async_Buffer_Tail THEN
- BEGIN (* No character to retrieve *)
-
- Async_Receive := FALSE;
- C := CHR( 0 );
-
- END (* No character available *)
-
- ELSE
- BEGIN (* Character available *)
-
- (* Turn off interrupts *)
-
- INLINE( $FA ); (* CLI --- Turn off interrupts *)
-
- (* Get character from buffer *)
-
- C := Async_Buffer[ Async_Buffer_Tail ];
-
- (* Increment buffer pointer. IF past *)
- (* end of buffer, reset to beginning. *)
-
- Async_Buffer_Tail := Async_Buffer_Tail + 1;
-
- IF Async_Buffer_Tail > Async_Buffer_Max THEN
- Async_Buffer_Tail := 0;
-
- (* Decrement buffer use count *)
-
- Async_Buffer_Used := Async_Buffer_Used - 1;
-
- (* Turn on interrupts *)
-
- INLINE( $FB ); (* STI --- Turn on interrupts *)
-
- (* Indicate character successfully retrieved *)
-
- Async_Receive := TRUE;
-
- END (* Character available *);
-
- END (* Async_Receive *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Receive_With_TimeOut --- Return char. from buffer with delay *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Receive_With_Timeout( Secs : INTEGER; VAR C : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Receive_With_Timeout *)
- (* *)
- (* Purpose: Retrieve character as integer from buffer, *)
- (* or return TimeOut if specified delay period *)
- (* expires. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Receive_With_Timeout( Secs: INTEGER; VAR C: INTEGER ); *)
- (* *)
- (* Secs --- Timeout period in seconds *)
- (* C --- ORD(character) (if any) retrieved from buffer; *)
- (* set to TimeOut if no character found before *)
- (* delay period expires. *)
- (* *)
- (* Calls: Async_Receive *)
- (* TimeOfDay *)
- (* *)
- (* WATCH OUT! THIS ROUTINE RETURNS AN INTEGER, NOT A CHARACTER!!! *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Ch : CHAR;
- Time_Limit : REAL;
- B : BOOLEAN;
-
- BEGIN (* Async_Receive_With_Timeout *)
-
- IF Async_Buffer_Head <> Async_Buffer_Tail THEN
- BEGIN
- B := Async_Receive( Ch );
- C := ORD( Ch );
- END
- ELSE
- BEGIN
- (* Convert time to milliseconds *)
-
- Time_Limit := Secs * 1000.0;
-
- WHILE ( Async_Buffer_Head = Async_Buffer_Tail ) AND
- ( Time_Limit > 0.0 ) DO
- BEGIN
- Delay( 1 );
- Time_Limit := Time_Limit - 1.0;
- END;
-
- IF ( Async_Buffer_Head <> Async_Buffer_Tail ) AND
- ( Time_Limit > 0.0 ) THEN
- BEGIN
- B := Async_Receive( Ch );
- C := ORD( Ch );
- END
- ELSE
- C := TimeOut;
-
- END;
-
- END (* Async_Receive_With_Timeout *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Ring_Detect --- Check for phone ringing *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Ring_Detect : BOOLEAN;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Ring_Detect *)
- (* *)
- (* Purpose: Looks for phone ringing *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Flag := Async_Ring_Detect : BOOLEAN; *)
- (* *)
- (* Flag is set TRUE if ringing detected, else FALSE. *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Ring_Detect *)
-
- Async_Ring_Detect := ODD( Port[ UART_MSR + Async_Base ] SHR 6 );
-
- END (* Async_Ring_Detect *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Send --- Send character over communications port *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Send( C : Char );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Send *)
- (* *)
- (* Purpose: Sends character out over communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Send( C : Char ); *)
- (* *)
- (* C --- Character to send *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- i : INTEGER;
- m : INTEGER;
- Counter : INTEGER;
-
- BEGIN (* Async_Send *)
-
- (* Turn on OUT2, DTR, and RTS *)
-
- Port[UART_MCR + Async_Base] := $0B;
-
- (* Wait for CTS using Busy Wait *)
-
- Counter := MaxInt;
-
- WHILE ( Counter <> 0 ) AND
- ( ( Port[UART_MSR + Async_Base] AND $10 ) = 0 ) DO
- Counter := Counter - 1;
-
- (* Wait for Transmit Hold Register Empty (THRE) *)
-
- IF Counter <> 0 THEN Counter := MaxInt;
-
- While ( Counter <> 0 ) AND
- ( ( Port[UART_LSR + Async_Base] AND $20 ) = 0 ) Do
- Counter := Counter - 1;
-
- (* Send the character if port clear *)
-
- IF Counter <> 0 THEN
- BEGIN (* Send the Character *)
-
- INLINE($FA); (* CLI --- disable interrupts *)
-
- Port[UART_THR + Async_Base] := Ord(C);
-
- INLINE($FB); (* STI --- enable interrupts *)
-
- END (* Send the Character *)
-
- ELSE (* Timed Out *)
- WRITELN('<<<TIMEOUT>>>');
-
- END (* Async_Send *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Send_Break --- Send break (attention) signal *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Send_Break;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Send_Break *)
- (* *)
- (* Purpose: Sends break signal over communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Send_Break; *)
- (* *)
- (* Calls: None *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- Old_Lcr : BYTE;
- Break_Lcr : BYTE;
-
- BEGIN (* Async_Send_Break *)
-
- Old_Lcr := Port[ UART_LCR + Async_Base ];
- Break_Lcr := Old_Lcr;
-
- IF Break_Lcr > 127 THEN Break_Lcr := Break_Lcr - 128;
- IF Break_Lcr <= 63 THEN Break_Lcr := Break_Lcr + 64;
-
- Port[ UART_LCR + Async_Base ] := Break_Lcr;
-
- Delay( 400 );
-
- Port[ UART_LCR + Async_Base ] := Old_Lcr;
-
- END (* Async_Send_Break *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Send_String --- Send string over communications port *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Send_String( S : AnyStr );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Send_String *)
- (* *)
- (* Purpose: Sends string out over communications port *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Send_String( S : AnyStr ); *)
- (* *)
- (* S --- String to send *)
- (* *)
- (* Calls: Async_Send *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- i : INTEGER;
-
- BEGIN (* Async_Send_String *)
-
- FOR i := 1 TO LENGTH( S ) DO
- Async_Send( S[i] )
-
- END (* Async_Send_String *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Send_String_With_Delays --- Send string with timed delays *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Send_String_With_Delays( S : AnyStr;
- Char_Delay : INTEGER;
- EOS_Delay : INTEGER );
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Send_String_With_Delays *)
- (* *)
- (* Purpose: Sends string out over communications port with *)
- (* specified delays for each character and at the *)
- (* end of the string. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Send_String_With_Delays( S : AnyStr ; *)
- (* Char_Delay : INTEGER; *)
- (* EOS_Delay : INTEGER ); *)
- (* *)
- (* S --- String to send *)
- (* Char_Delay --- Number of milliseconds to delay after *)
- (* sending each character *)
- (* EOS_Delay --- Number of milleseconds to delay after *)
- (* sending last character in string *)
- (* *)
- (* Calls: Async_Send *)
- (* Async_Send_String *)
- (* Length *)
- (* Delay *)
- (* *)
- (* Remarks: *)
- (* *)
- (* This routine is useful when writing routines to perform *)
- (* non-protocol uploads. Many computer systems require delays *)
- (* between receipt of characters for correct processing. The *)
- (* delay for end-of-string usually applies when the string *)
- (* represents an entire line of a file. *)
- (* *)
- (* If delays are not required, Async_Send_String is faster. *)
- (* This routine will call Async_Send_String is no character *)
- (* delay is to be done. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- I : INTEGER;
-
- BEGIN (* Async_Send_String_With_Delays *)
-
- IF Char_Delay <= 0 THEN
- Async_Send_String( S )
- ELSE
- FOR I := 1 TO LENGTH( S ) DO
- BEGIN
- Async_Send( S[I] );
- Delay( Char_Delay );
- END;
-
- IF EOS_Delay > 0 THEN Delay( EOS_Delay );
-
- END (* Async_Send_String_With_Delays *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Percentage_Used --- Report Percentage Buffer Filled *)
- (*----------------------------------------------------------------------*)
-
- FUNCTION Async_Percentage_Used : REAL;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Function: Async_Percent_Used *)
- (* *)
- (* Purpose: Reports percentage of com buffer currently filled *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Percentage := Async_Percentage_Used : Real; *)
- (* *)
- (* Percentage gets how much of buffer is filled; *)
- (* value goes from 0.0 (empty) to 1.0 (totally full). *)
- (* *)
- (* Calls: None *)
- (* *)
- (* Remarks: *)
- (* *)
- (* This routine is helpful when incorporating handshaking into *)
- (* a communications program. For example, assume that the host *)
- (* computer uses the XON/XOFF (DC1/DC3) protocol. Then the *)
- (* PC program should issue an XOFF to the host when the value *)
- (* returned by Async_Percentage_Used > .75 or so. When the *)
- (* utilization percentage drops below .25 or so, the PC program *)
- (* should transmit an XON. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Percentage_Used *)
-
- Async_Percentage_Used := Async_Buffer_Used / ( Async_Buffer_Max + 1 );
-
- END (* Async_Percentage_Used *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Purge_Buffer --- Purge communications input buffer *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Purge_Buffer;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Purge_Buffer *)
- (* *)
- (* Purpose: Purges communications input buffer *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Purge_Buffer; *)
- (* *)
- (* Calls: Async_Receive *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- VAR
- C: CHAR;
- L: INTEGER;
-
- BEGIN (* Async_Purge_Buffer *)
-
- L := 10000 DIV Async_Baud_Rate;
-
- REPEAT
- Delay( L );
- UNTIL ( NOT Async_Receive( C ) );
-
- END (* Async_Purge_Buffer *);
-
- (*----------------------------------------------------------------------*)
- (* Async_Buffer_Full --- Check if com buffer nearly full *)
- (*----------------------------------------------------------------------*)
-
- PROCEDURE Async_Buffer_Full;
-
- (*----------------------------------------------------------------------*)
- (* *)
- (* Procedure: Async_Buffer_Full *)
- (* *)
- (* Purpose: Check if buffer nearly full, issue XOFF if so. *)
- (* *)
- (* Calling Sequence: *)
- (* *)
- (* Async_Buffer_Full; *)
- (* *)
- (* Calls: Async_Send *)
- (* *)
- (* Remarks: *)
- (* *)
- (* An XOFF if issued if the buffer is nearly full and an XOFF *)
- (* has not been previously issued. If an XOFF was previously *)
- (* issued, and the buffer is reasonably empty, then an XON *)
- (* is issued. *)
- (* *)
- (*----------------------------------------------------------------------*)
-
- BEGIN (* Async_Buffer_Full *)
-
- IF ( Async_Buffer_Used * 4 ) > ( Async_Buffer_Max * 3 ) THEN
- BEGIN (* Buffer too full -- send XOFF if we already haven't *)
- IF ( NOT Async_XOFF_Sent ) THEN
- BEGIN
- Async_Send( Async_XOFF );
- Async_XOFF_Sent := TRUE;
- END
- END (* Buffer too full *)
- ELSE IF ( Async_Buffer_Used * 4 ) < Async_Buffer_Max THEN
- BEGIN (* Buffer reasonably empty -- send XON if needed *)
- IF Async_XOFF_Sent THEN
- BEGIN
- Async_Send( Async_XON );
- Async_XOFF_Sent := FALSE;
- END;
- END;
-
- END (* Async_Buffer_Full *);